<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Ajax Put 上传（服务端计算签名）</title>
    <style>
      h1,
      h2 {
        font-weight: normal;
      }

      #msg {
        margin-top: 10px;
      }
    </style>
  </head>
  <body>
    <h1>Ajax Put 上传（服务端计算签名）</h1>

    <input id="fileSelector" type="file" />
    <input id="submitBtn" type="submit" />

    <div id="msg"></div>

    <script>
      (function () {
        // 对更多字符编码的 url encode 格式
        const camSafeUrlEncode = function (str) {
          return encodeURIComponent(str)
            .replace(/!/g, '%21')
            .replace(/'/g, '%27')
            .replace(/\(/g, '%28')
            .replace(/\)/g, '%29')
            .replace(/\*/g, '%2A');
        };

        // 计算签名
        const getAuthorization = function (opt, callback) {
          // 替换为自己服务端地址 获取put上传签名
          const url = `http://127.0.0.1:3000/put-sign?ext=${opt.ext}`;
          const xhr = new XMLHttpRequest();
          xhr.open('GET', url, true);
          xhr.onload = function (e) {
            let credentials;
            try {
              const result = JSON.parse(e.target.responseText);
              credentials = result;
            } catch (e) {
              callback('获取签名出错');
            }
            if (credentials) {
              callback(null, {
                securityToken: credentials.securityToken,
                authorization: credentials.authorization,
                cosKey: credentials.cosKey,
                cosHost: credentials.cosHost,
              });
            } else {
              console.error(xhr.responseText);
              callback('获取签名出错');
            }
          };
          xhr.onerror = function (e) {
            callback('获取签名出错');
          };
          xhr.send();
        };

        // 上传文件
        const uploadFile = function (file, callback) {
          const fileName = file.name;
          let ext = '';
          const lastDotIndex = fileName.lastIndexOf('.');
          if (lastDotIndex > -1) {
            // 这里获取文件后缀 由服务端生成最终上传的路径
            ext = fileName.substring(lastDotIndex + 1);
          }
          getAuthorization({ ext }, function (err, info) {
            if (err) {
              alert(err);
              return;
            }
            const auth = info.authorization;
            const securityToken = info.securityToken;
            const Key = info.cosKey;
            const protocol =
              location.protocol === 'https:' ? 'https:' : 'http:';
            const prefix = protocol + '//' + info.cosHost;
            const url =
              prefix + '/' + camSafeUrlEncode(Key).replace(/%2F/g, '/');
            const xhr = new XMLHttpRequest();
            xhr.open('PUT', url, true);
            xhr.setRequestHeader('Authorization', auth);
            securityToken &&
              xhr.setRequestHeader('x-cos-security-token', securityToken);
            xhr.upload.onprogress = function (e) {
              console.log(
                '上传进度 ' +
                  Math.round((e.loaded / e.total) * 10000) / 100 +
                  '%'
              );
            };
            xhr.onload = function () {
              if (/^2\d\d$/.test('' + xhr.status)) {
                const ETag = xhr.getResponseHeader('etag');
                callback(null, { url: url, ETag: ETag });
              } else {
                callback('文件 ' + Key + ' 上传失败，状态码：' + xhr.status);
              }
            };
            xhr.onerror = function () {
              callback(
                '文件 ' + Key + ' 上传失败，请检查是否没配置 CORS 跨域规则'
              );
            };
            xhr.send(file);
          });
        };

        // 监听表单提交
        document.getElementById('submitBtn').onclick = function (e) {
          const file = document.getElementById('fileSelector').files[0];
          if (!file) {
            document.getElementById('msg').innerText = '未选择上传文件';
            return;
          }
          file &&
            uploadFile(file, function (err, data) {
              console.log(err || data);
              document.getElementById('msg').innerText = err
                ? err
                : '上传成功，ETag=' + data.ETag;
            });
        };
      })();
    </script>
  </body>
</html>
